/*
 * A Very Very Simple Filesystem Zwei
 * Original By: Eric McCreath 2006, 2008, 2010 - GPL
 *
 * (based on the simplistic RAM filesystem McCreath 2001)
 */

/* to make use:
 *    make -C /usr/src/linux-headers-2.6.32-23-generic/  SUBDIRS=$PWD modules
 *    (or just make, with the accompanying Makefile)
 *
 * to load use:
 *    sudo insmod vvsfs.ko
 *    (may need to copy vvsfs.ko to a local filesystem first)
 *
 * to make a suitable filesystem:
 *    dd of=myvvsfs.raw if=/dev/zero bs=512 count=100
 *    ./mkfs.vvsfs myvvsfs.raw
 *    (could also use a USB device etc.)
 *
 * to mount use:
 *    mkdir testdir
 *    sudo mount -o loop -t vvsfs myvvsfs.raw testdir
 *
 * to use a USB device:
 *    create a suitable partition on USB device (exercise for reader)
 *    ./mkfs.vvsfs /dev/sdXn
 *    where sdXn is the device name of the usb drive
 *    mkdir testdir
 *    sudo mount -t vvsfs /dev/sdXn testdir
 *
 * use the file system:
 *    cd testdir
 *    echo hello > file1
 *    cat file1
 *    cd ..
 *
 * unmount the filesystem:
 *    sudo umount testdir
 *
 * remove the module:
 *    sudo rmmod vvsfs
 */

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/lz4.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/statfs.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/quotaops.h>
#include <linux/namei.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>

#include "vvsfs.h"

#define DEBUG 1

struct vvsfs_mounted_info {
    struct super_block *sb;
    char *compress_cache;
    uint flag;
    uint block_content_size;
    uint8_t aes_key[32];
    int aes_key_length;
    struct vvsfs_device_block device_info;
    spinlock_t sb_lock;
    uint ssd_start_empty_block;
    struct vvsfs_mounted_info *next;
};

static struct inode_operations vvsfs_file_inode_operations;
static struct inode_operations vvsfs_file_symbol_link_operations;
static struct inode_operations vvsfs_dir_inode_operations;
static struct inode_operations vvsfs_special_inode_operations;
static struct file_operations vvsfs_file_operations;
static struct file_operations vvsfs_dir_operations;
static struct super_operations vvsfs_ops;

char *vvsfs_storage_unit[] = {
    "Bytes",
    "KB",
    "MB",
    "GB"
};

static struct vvsfs_mounted_info *mount_list=NULL;

static void vvsfs_update_device_info(struct super_block * sb);
static struct inode *vvsfs_iget(struct super_block *sb, unsigned long ino);
struct inode *vvsfs_new_inode(const struct inode * dir, umode_t mode);
static ssize_t
vvsfs_file_write_content(struct inode *inode, int file_flags, const char *buf, size_t count, loff_t *ppos, bool from_user);
static ssize_t
vvsfs_file_read_content(struct inode *inode, char **buf, size_t *count, loff_t *ppos, bool to_user);
static bool
vvsfs_get_inode(struct inode *i, struct vvsfs_inode *inode);

//----------------AES-256 bit encryption----------------
typedef struct {
    uint8_t key[32];
    uint8_t enckey[32];
    uint8_t deckey[32];
} vvsfs_aes256_context;

#define FD(x)  (((x) >> 1) ^ (((x) & 1) ? 0x8d : 0))

static uint8_t vvsfs_rj_xtime(uint8_t);
static void vvsfs_aes_subBytes(uint8_t *);
static void vvsfs_aes_subBytes_inv(uint8_t *);
static void vvsfs_aes_addRoundKey(uint8_t *, uint8_t *);
static void vvsfs_aes_addRoundKey_cpy(uint8_t *, uint8_t *, uint8_t *);
static void vvsfs_aes_shiftRows(uint8_t *);
static void vvsfs_aes_shiftRows_inv(uint8_t *);
static void vvsfs_aes_mixColumns(uint8_t *);
static void vvsfs_aes_mixColumns_inv(uint8_t *);
static void vvsfs_aes_expandEncKey(uint8_t *, uint8_t *);
static void vvsfs_aes_expandDecKey(uint8_t *, uint8_t *);

static const uint8_t sbox[256] =
{
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
    0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
    0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
    0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
    0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
    0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
    0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
    0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
    0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
    0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
    0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
    0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
    0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
    0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
    0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
    0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
    0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};
static const uint8_t sboxinv[256] =
{
    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
    0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
    0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
    0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
    0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
    0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
    0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
    0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
    0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
    0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
    0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
    0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
    0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
    0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
    0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
    0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
    0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
    0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
    0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
    0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
    0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
    0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
    0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
    0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
    0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
    0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
    0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};

#define rj_sbox(x)     sbox[(x)]
#define rj_sbox_inv(x) sboxinv[(x)]

static uint8_t vvsfs_rj_xtime(uint8_t x)
{
    uint8_t y = (uint8_t)(x << 1);
    return (x & 0x80) ? (y ^ 0x1b) : y;
}

static void vvsfs_aes_subBytes(uint8_t *buf)
{
    register uint8_t i = 16;
    while (i--) buf[i] = rj_sbox(buf[i]);
}

static void vvsfs_aes_subBytes_inv(uint8_t *buf)
{
    register uint8_t i = 16;
    while (i--) buf[i] = rj_sbox_inv(buf[i]);
}

static void vvsfs_aes_addRoundKey(uint8_t *buf, uint8_t *key)
{
    register uint8_t i = 16;
    while (i--) buf[i] ^= key[i];
}

static void vvsfs_aes_addRoundKey_cpy(uint8_t *buf, uint8_t *key, uint8_t *cpk)
{
    register uint8_t i = 16;
    while (i--)  buf[i] ^= (cpk[i] = key[i]), cpk[16 + i] = key[16 + i];
}

static void vvsfs_aes_shiftRows(uint8_t *buf)
{
    register uint8_t i, j;
    i = buf[1], buf[1] = buf[5], buf[5] = buf[9], buf[9] = buf[13], buf[13] = i;
    i = buf[10], buf[10] = buf[2], buf[2] = i;
    j = buf[3], buf[3] = buf[15], buf[15] = buf[11], buf[11] = buf[7], buf[7] = j;
    j = buf[14], buf[14] = buf[6], buf[6]  = j;

}

static void vvsfs_aes_shiftRows_inv(uint8_t *buf)
{
    register uint8_t i, j;
    i = buf[1], buf[1] = buf[13], buf[13] = buf[9], buf[9] = buf[5], buf[5] = i;
    i = buf[2], buf[2] = buf[10], buf[10] = i;
    j = buf[3], buf[3] = buf[7], buf[7] = buf[11], buf[11] = buf[15], buf[15] = j;
    j = buf[6], buf[6] = buf[14], buf[14] = j;

}

static void vvsfs_aes_mixColumns(uint8_t *buf)
{
    register uint8_t i, a, b, c, d, e;
    for (i = 0; i < 16; i += 4)
    {
        a = buf[i];
        b = buf[i + 1];
        c = buf[i + 2];
        d = buf[i + 3];
        e = a ^ b ^ c ^ d;
        buf[i] ^= e ^ vvsfs_rj_xtime(a ^ b);
        buf[i + 1] ^= e ^ vvsfs_rj_xtime(b ^ c);
        buf[i + 2] ^= e ^ vvsfs_rj_xtime(c ^ d);
        buf[i + 3] ^= e ^ vvsfs_rj_xtime(d ^ a);
    }
}

static void vvsfs_aes_mixColumns_inv(uint8_t *buf)
{
    register uint8_t i, a, b, c, d, e, x, y, z;

    for (i = 0; i < 16; i += 4)
    {
        a = buf[i];
        b = buf[i + 1];
        c = buf[i + 2];
        d = buf[i + 3];
        e = a ^ b ^ c ^ d;
        z = vvsfs_rj_xtime(e);
        x = e ^ vvsfs_rj_xtime(vvsfs_rj_xtime(z ^ a ^ c));
        y = e ^ vvsfs_rj_xtime(vvsfs_rj_xtime(z ^ b ^ d));
        buf[i] ^= x ^ vvsfs_rj_xtime(a ^ b);
        buf[i + 1] ^= y ^ vvsfs_rj_xtime(b ^ c);
        buf[i + 2] ^= x ^ vvsfs_rj_xtime(c ^ d);
        buf[i + 3] ^= y ^ vvsfs_rj_xtime(d ^ a);
    }
}

static void vvsfs_aes_expandEncKey(uint8_t *k, uint8_t *rc)
{
    register uint8_t i;
    k[0] ^= rj_sbox(k[29]) ^ (*rc);
    k[1] ^= rj_sbox(k[30]);
    k[2] ^= rj_sbox(k[31]);
    k[3] ^= rj_sbox(k[28]);
    *rc = vvsfs_rj_xtime( *rc);
    for(i = 4; i < 16; i += 4)  k[i] ^= k[i - 4],   k[i + 1] ^= k[i - 3],
                                            k[i + 2] ^= k[i - 2], k[i + 3] ^= k[i - 1];
    k[16] ^= rj_sbox(k[12]);
    k[17] ^= rj_sbox(k[13]);
    k[18] ^= rj_sbox(k[14]);
    k[19] ^= rj_sbox(k[15]);
    for(i = 20; i < 32; i += 4) k[i] ^= k[i - 4],   k[i + 1] ^= k[i - 3],
                                            k[i + 2] ^= k[i - 2], k[i + 3] ^= k[i - 1];

}

static void vvsfs_aes_expandDecKey(uint8_t *k, uint8_t *rc)
{
    uint8_t i;
    for(i = 28; i > 16; i -= 4) k[i + 0] ^= k[i - 4], k[i + 1] ^= k[i - 3],
                                                k[i + 2] ^= k[i - 2], k[i + 3] ^= k[i - 1];

    k[16] ^= rj_sbox(k[12]);
    k[17] ^= rj_sbox(k[13]);
    k[18] ^= rj_sbox(k[14]);
    k[19] ^= rj_sbox(k[15]);
    for(i = 12; i > 0; i -= 4)  k[i + 0] ^= k[i - 4], k[i + 1] ^= k[i - 3],
                                                k[i + 2] ^= k[i - 2], k[i + 3] ^= k[i - 1];

    *rc = FD(*rc);
    k[0] ^= rj_sbox(k[29]) ^ (*rc);
    k[1] ^= rj_sbox(k[30]);
    k[2] ^= rj_sbox(k[31]);
    k[3] ^= rj_sbox(k[28]);
}

static void vvsfs_aes256_init(vvsfs_aes256_context *ctx, uint8_t *k)
{
    uint8_t rcon = 1;
    register uint8_t i;

    for (i = 0; i < sizeof(ctx->key); i++) ctx->enckey[i] = ctx->deckey[i] = k[i];
    for (i = 8; --i;) vvsfs_aes_expandEncKey(ctx->deckey, &rcon);
}

static void vvsfs_aes256_done(vvsfs_aes256_context *ctx)
{
    register uint8_t i;

    for (i = 0; i < sizeof(ctx->key); i++)
        ctx->key[i] = ctx->enckey[i] = ctx->deckey[i] = 0;
}

static void vvsfs_aes256_encrypt_ecb(vvsfs_aes256_context *ctx, uint8_t *buf)
{
    uint8_t i, rcon;

    vvsfs_aes_addRoundKey_cpy(buf, ctx->enckey, ctx->key);
    for(i = 1, rcon = 1; i < 14; ++i)
    {
        vvsfs_aes_subBytes(buf);
        vvsfs_aes_shiftRows(buf);
        vvsfs_aes_mixColumns(buf);
        if( i & 1 ) vvsfs_aes_addRoundKey( buf, &ctx->key[16]);
        else vvsfs_aes_expandEncKey(ctx->key, &rcon), vvsfs_aes_addRoundKey(buf, ctx->key);
    }
    vvsfs_aes_subBytes(buf);
    vvsfs_aes_shiftRows(buf);
    vvsfs_aes_expandEncKey(ctx->key, &rcon);
    vvsfs_aes_addRoundKey(buf, ctx->key);
}

static void vvsfs_aes256_decrypt_ecb(vvsfs_aes256_context *ctx, uint8_t *buf)
{
    uint8_t i, rcon;
    vvsfs_aes_addRoundKey_cpy(buf, ctx->deckey, ctx->key);
    vvsfs_aes_shiftRows_inv(buf);
    vvsfs_aes_subBytes_inv(buf);
    for (i = 14, rcon = 0x80; --i;)
    {
        if( ( i & 1 ) )
        {
            vvsfs_aes_expandDecKey(ctx->key, &rcon);
            vvsfs_aes_addRoundKey(buf, &ctx->key[16]);
        }
        else vvsfs_aes_addRoundKey(buf, ctx->key);
        vvsfs_aes_mixColumns_inv(buf);
        vvsfs_aes_shiftRows_inv(buf);
        vvsfs_aes_subBytes_inv(buf);
    }
    vvsfs_aes_addRoundKey( buf, ctx->key);
}
//--------------AES 256 Encryption--------------
/*
 * vvsfs_hash - Calculate the hash result of one block.
 * Base on Peter J. Weinberger's hash function (from the Dragon book).
 */
static uint vvsfs_hash(const uchar *p, int n) {
    uint h=0;
    uint g;
    while(n--) {
        h = (h << 4) + *p++;
        if((g=(h & 0xf0000000)) != 0)
            h ^= g >> 23;
        h &= ~g;
    }
    return h;
}

// vvsfs_block_hash - get the value of the block hash.
static inline uint vvsfs_block_hash(struct vvsfs_block *block){
    return vvsfs_hash(block->data, block->size);
}

static void vvsfs_set_operations(struct inode *inode) {
    if (S_ISDIR(inode->i_mode)) {
        if(DEBUG) printk("Set operations as DIR.\n");
        inode->i_op = &vvsfs_dir_inode_operations;
        inode->i_fop = &vvsfs_dir_operations;
    } else if(S_ISLNK(inode->i_mode)) {
        if(DEBUG) printk("Set operations as SYMLINK.\n");
        inode->i_op = &vvsfs_file_symbol_link_operations;
        inode->i_fop = &vvsfs_file_operations;
    } else if(S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
        if(DEBUG) printk("Set operations as Device.\n");
        inode->i_op=&vvsfs_special_inode_operations;
    } else {
        if(DEBUG) printk("Set operations as REGFILE.\n");
        inode->i_op = &vvsfs_file_inode_operations;
        inode->i_fop = &vvsfs_file_operations;
    }
}

static void
vvsfs_put_super(struct super_block *sb) {
    if (DEBUG) printk("vvsfs - put_super\n");
    return;
}

static int 
vvsfs_statfs(struct dentry *dentry, struct kstatfs *buf) {
    if (DEBUG) printk("vvsfs - statfs\n");
    return 0;
}

// vvsfs_alloc_block - Allocate a block size large memory for cache.
struct vvsfs_block *vvsfs_alloc_block(struct super_block *sb) {
    return kmalloc(((struct vvsfs_mounted_info *)
                    sb->s_fs_info)->device_info.block_size,
                   GFP_KERNEL);
}

// vvsfs_free_block - Free the block pointer.
void vvsfs_free_block(struct vvsfs_block **block) {
    kfree(*block);
    (*block)=NULL;
}

//vvsfs_readrawblock - read any block to a row content.
static int
vvsfs_readrawblock(struct super_block *sb, int num, void *block) {
    struct buffer_head *bh;
    uint block_size=((struct vvsfs_mounted_info *)
                     sb->s_fs_info)->device_info.block_size;
    //Read from super block.
    bh = sb_bread(sb, num);
    //Copy the data to the block cache.
    memcpy((void *) block, (void *) bh->b_data, block_size);
    //Release the buf.
    brelse(bh);
    return block_size;
}

// vvsfs_readblock - reads a block from the block device (this will copy over
//                      the top of inode)
static int
vvsfs_readblock(struct super_block *sb, int inum, struct vvsfs_block *block) {
    struct vvsfs_mounted_info *mounted_info=
            (struct vvsfs_mounted_info *)sb->s_fs_info;
    static vvsfs_aes256_context aes_context;
    int decryptLength, endPos, i, j, ret;

    if (DEBUG) printk("vvsfs - readblock : %d\n", inum);
    //Read raw block information.
    ret=vvsfs_readrawblock(sb, inum, block);
    //Check encrypted flag.
    //File System should also enable encrypt.
    if(block->flag & VVSFS_FLAG_ENCRYPT) {
        //Also has valid key.
        if(mounted_info->aes_key_length > 0) {
            //Calculate decrypt length.
            decryptLength=(block->e_size==-1)?block->size:block->e_size;
            //Initial the AES-256 Context.
            vvsfs_aes256_init(&aes_context, mounted_info->aes_key);
            //Reset positions.
            i=0;
            //Decrypt the block data.
            while(i<decryptLength) {
                //Calculate the end position.
                endPos=i+16;
                //Check the end position.
                if(endPos<=decryptLength) {
                    //AES-256 decryption.
                    vvsfs_aes256_decrypt_ecb(&aes_context,
                                             (uint8_t *)(block->data+i));
                } else {
                    //XOR decryption.
                    for(j=i; j<decryptLength; ++j) {
                        //Use xor to decrypt.
                        block->data[j]^=mounted_info->aes_key[j-i];
                    }
                }
                //Update i.
                i=endPos;
            }
            //Done AES-256 encrypt.
            vvsfs_aes256_done(&aes_context);
            if (DEBUG) printk("decrypted block\n");
        } else {
            return 0;
        }
    }
    if (DEBUG) printk("vvsfs - readblock done : %d\n", inum);
    return ret;
}

static int
vvsfs_writerawblock(struct super_block *sb, int inum, void *block){
    struct buffer_head *bh;
    uint block_size=((struct vvsfs_mounted_info *)
                     sb->s_fs_info)->device_info.block_size;

    bh = sb_bread(sb,inum);
    memcpy(bh->b_data, block, block_size);
    mark_buffer_dirty(bh);
    sync_dirty_buffer(bh);
    brelse(bh);
    return block_size;
}

// vvsfs_writeblock - write a block from the block device(this will just mark the block
//                      as dirtycopy)
static int
vvsfs_writeblock(struct super_block *sb, int inum, struct vvsfs_block *block) {
    struct vvsfs_mounted_info *mounted_info=
            (struct vvsfs_mounted_info *)sb->s_fs_info;
    static vvsfs_aes256_context aes_context;
    static uint8_t aes_buf[16];
    int i, j, endPos, bufLength, remainLength, ret;

    if (DEBUG) printk("vvsfs - writeblock : %d\n", inum);

    //Check the encryption flag.
    if(block->flag & VVSFS_FLAG_ENCRYPT) {
        //Reset the flag.
        block->flag &= ~VVSFS_FLAG_ENCRYPT;
    }
    block->e_size=-1;
    //Check the file system flag.
    if(mounted_info->flag & VVSFS_MOUNT_FLAG_ENCRYPT &&
            mounted_info->aes_key_length > 0) {
        if(DEBUG) printk("Start to encrypt block\n");
        //Set the encrypt flag
        block->flag |= VVSFS_FLAG_ENCRYPT;
        //Generate the AES Context.
        vvsfs_aes256_init(&aes_context, mounted_info->aes_key);
        //Reset position.
        i=0;
        //Encrypt the block data.
        while(i<block->size) {
            //Calculate the end position.
            endPos=i+16;
            //Check end position.
            if(endPos<block->size) {
                //Do AES-256 encryption.
                vvsfs_aes256_encrypt_ecb(&aes_context,
                                         (uint8_t *)(block->data+i));
            } else {
                //Check do we have space to use AES-256
                if(endPos < mounted_info->block_content_size) {
                    //Clear the buf area.
                    bufLength=sizeof(aes_buf);
                    memset(aes_buf, 0, bufLength);
                    //Set the remain data to buf.
                    remainLength=block->size-i;
                    memcpy(aes_buf, block->data+i, remainLength);
                    //Encrypt the buf.
                    vvsfs_aes256_encrypt_ecb(&aes_context, aes_buf);
                    //Copy the data to original pos.
                    memcpy(block->data+i, aes_buf, bufLength);
                    //Update encryption size.
                    block->e_size=block->size-remainLength+bufLength;
                } else {
                    //No enough length, use XOR encryption.
                    for(j=i; j<block->size; ++j) {
                        block->data[j]^=mounted_info->aes_key[j-i];
                    }
                }
            }
            //Update i.
            i=endPos;
        }
        //Encryption done.
        vvsfs_aes256_done(&aes_context);
        if (DEBUG) printk("block encrypted.");
    }
    //Write the block to the position.
    ret=vvsfs_writerawblock(sb, inum, block);
    if (DEBUG) printk("vvsfs - writeblock done: %d\n", inum);
    return ret;
}

// vvsfs_dentry_size - calculate the dentry size.
static inline int
vvsfs_dentry_size (struct vvsfs_dir_entry *dentry) {
    return sizeof(struct vvsfs_dir_entry)+dentry->name_size*sizeof(char);
}

static int vvsfs_set_journal_state(uint journal_state,
                                   struct super_block *sb) {
    struct vvsfs_block *journal_state_block=NULL;
    struct vvsfs_journal_state *journal_log;

    journal_state_block=vvsfs_alloc_block(sb);
    if(journal_state_block==NULL) {
        return -ENOMEM;
    }
    //Set the journal log.
    journal_log=(struct vvsfs_journal_state *)journal_state_block;

    if(DEBUG) printk("vvsfs - set journal state, state: %d.\n", journal_state);
    //Read and set the block, write it back.
    vvsfs_readrawblock(sb, BLOCK_JOURNAL_STATE, journal_state_block);
    journal_log->state=journal_state;
    vvsfs_writerawblock(sb, BLOCK_JOURNAL_STATE, journal_state_block);
    //Free the mem.
    vvsfs_free_block(&journal_state_block);
    if(DEBUG) printk("vvsfs - set journal state done.\n");
    return 0;
}

static int vvsfs_start_journal(uint journal_op,
                               struct inode *inode) {
    struct vvsfs_block *journal_block=NULL;
    struct vvsfs_journal_state *journal_state;
    struct vvsfs_inode file_inode;
    struct super_block *sb=inode->i_sb;

    journal_block=vvsfs_alloc_block(sb);
    if(journal_block==NULL) {
        return -ENOMEM;
    }
    //Set the state point to block.
    journal_state=(struct vvsfs_journal_state *)journal_block;

    //Read the state block.
    journal_state->state=JOURNAL_LOGGING;
    //Write to disk.
    vvsfs_writerawblock(sb, BLOCK_JOURNAL_STATE, journal_block);
    //Write the journal data.
    journal_state->file_operation=journal_op;
    //Copy the data.
    journal_state->inode_number=inode->i_ino;
    vvsfs_get_inode(inode, &file_inode);
    memcpy((void *)(&(journal_state->inode)),
           &file_inode,
           sizeof(struct vvsfs_inode));
    //Write to disk.
    vvsfs_writerawblock(sb, BLOCK_JOURNAL_STATE, journal_block);

    //Write the content to journal data area.
    vvsfs_readrawblock(sb, file_inode.block, journal_block);
    vvsfs_writerawblock(sb, BLOCK_JOURNAL_DATA, journal_block);

    //Journal start complete, switch to writing state.
    vvsfs_readrawblock(sb, BLOCK_JOURNAL_STATE, journal_block);
    journal_state->state=JOURNAL_WRITING;
    vvsfs_writerawblock(sb, BLOCK_JOURNAL_STATE, journal_block);
    return 0;
}

// vvsfs_find_block - find one block which is using and the content hash is exactly the same.
static int
vvsfs_find_block(struct super_block *sb, uint hash, uint flag) {
    struct vvsfs_mounted_info *mounted_info=
            (struct vvsfs_mounted_info *)sb->s_fs_info;
    int i, root_block=mounted_info->device_info.inode_block_count +
            BLOCK_INODE_LIST;
    struct vvsfs_block *block=vvsfs_alloc_block(sb);

    if(block==NULL) {
        return -ENOMEM;
    }
    //Get the block information.
    for(i=root_block; i<mounted_info->device_info.block_count; ++i) {
        vvsfs_readrawblock(sb, i, block);
        //Check block data.
        if(!(block->flag & VVSFS_FLAG_ISEMPTY) && //Block is in use.
                block->hash == hash &&
                block->flag == flag) {
            //Free the block
            vvsfs_free_block(&block);
            //Find the target.
            return i;
        }
    }
    //Cannot find the block.
    vvsfs_free_block(&block);
    return -1;
}

// vvsfs_get_inode - Get inode information from vfs inode.
static bool
vvsfs_get_inode(struct inode *i, struct vvsfs_inode *inode) {
    int inode_id=i->i_ino;
    struct super_block *sb=i->i_sb;
    struct vvsfs_block *block=NULL;
    int inode_max_count=((struct vvsfs_mounted_info *)
                         sb->s_fs_info)->device_info.block_size /
                        sizeof(struct vvsfs_inode);
    int target_block_no=inode_id/inode_max_count;
    int target_block_offset=inode_id-target_block_no*inode_max_count;
    struct vvsfs_inode *inode_src;

    if (DEBUG) printk("vvsfs - get inode\n");
    //Allocate memory for one block.
    block=vvsfs_alloc_block(sb);
    if(block==NULL) {
        //Error happened.
        printk("No enough memory for getting inode.");
        return false;
    }
    //Read the inode block.
    vvsfs_readrawblock(sb, BLOCK_INODE_LIST+target_block_no, block);
    //Set the inode source to the position.
    inode_src=(struct vvsfs_inode *)block;
    //Move the source.
    inode_src+=target_block_offset;
    //Copy the data.
    memcpy(inode, inode_src, sizeof(struct vvsfs_inode));
    //Free the block.
    vvsfs_free_block(&block);
    //Finished.
    if (DEBUG) printk("vvsfs - get inode done\n");
    return true;
}

static bool
__vvsfs_set_inode(struct super_block *sb, int inode_id,
                  struct vvsfs_inode *inode) {
    struct vvsfs_block *block=NULL;
    struct vvsfs_inode *inode_dest;
    int inode_max_count=((struct vvsfs_mounted_info *)
                         sb->s_fs_info)->device_info.block_size /
                        sizeof(struct vvsfs_inode);
    int target_block_no=inode_id/inode_max_count;
    int target_block_offset=inode_id-target_block_no*inode_max_count;

    if (DEBUG) printk("vvsfs - set inode\n");
    block=vvsfs_alloc_block(sb);
    if(block==NULL) {
        //Error happened.
        printk("No enough memory for getting inode.");
        return false;
    }
    printk("inode position: %d block, %d offset.\n",
           target_block_no, target_block_offset);
    //Read the inode block.
    vvsfs_readrawblock(sb, BLOCK_INODE_LIST+target_block_no, block);
    //Set the inode source to the position.
    inode_dest=(struct vvsfs_inode *)block;
    //Move the source.
    inode_dest+=target_block_offset;
    printk("vvsfs inode size: %d\n", sizeof(struct vvsfs_inode));
    //Copy the data to destination.
    memcpy(inode_dest, inode, sizeof(struct vvsfs_inode));
    //Write the inode block.
    vvsfs_writerawblock(sb, BLOCK_INODE_LIST+target_block_no, block);
    //Free the block.
    vvsfs_free_block(&block);
    if (DEBUG) printk("vvsfs - set inode done\n");
    return true;
}

static bool
vvsfs_set_inode(struct inode *i, struct vvsfs_inode *inode) {
    return __vvsfs_set_inode(i->i_sb, i->i_ino, inode);
}

static int
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
// vvsfs_readdir - reads a directory and places the result using filldir
vvsfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
#else
vvsfs_readdir(struct file *filp, struct dir_context *ctx)
#endif
{
    struct inode *i;
    struct vvsfs_dir_entry *dent;
    int error, dent_size, dir_size=0;
    char *dent_pos, *dent_end_pos, *dir_data=NULL;
    loff_t dir_offset=0;

    if (DEBUG) printk("vvsfs - readdir\n");

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
    i = filp->f_dentry->d_inode;
#else
    i = file_inode(filp);
#endif
    //Read the raw directory content.
    if(vvsfs_file_read_content(i, &dir_data, &dir_size, &dir_offset, false)<0) {
        //Check pointer.
        if(dir_data) {
            //Free the dir data.
            kfree(dir_data);
        }
        return -EINVAL;
    }
    //Get the vvsfs inode.
    if (DEBUG) printk("fpos %Ld\n", filp->f_pos);

    error = 0;
    dent_pos=dir_data;
    dent_end_pos=dir_data+dir_size;
    dent = (struct vvsfs_dir_entry *)dent_pos;
    while (!error && filp->f_pos < dir_size && dent_pos<dent_end_pos) {
        printk("adding ino : %d\n", dent->inode_number);
        dent_size=vvsfs_dentry_size(dent);
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
        error = filldir(dirent,
                        dent->name, dent->name_size, filp->f_pos, dent->inode_number,DT_REG);
        if (error)
            break;
        filp->f_pos += dent_size;
#else
        if (dent->inode_number) {
            if (!dir_emit (ctx, dent->name, dent->name_size,
                           dent->inode_number, DT_UNKNOWN)) return 0;
        }
        ctx->pos += dent_size;
#endif
        dent_pos+=dent_size;
        dent=(struct vvsfs_dir_entry *)dent_pos;
    }
    //Update file size.
    i->i_size=dir_size;
    i->i_blocks=1;
    mark_inode_dirty(i);
    //Recover memory.
    kfree(dir_data);
    // update_atime(i);
    printk("done readdir\n");
    return 0;
}

// vvsfs_lookup - A directory name in a directory. It basically attaches the inode 
//                of the file to the directory entry.
static struct dentry *
vvsfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
{
    struct inode *inode = NULL;
    struct vvsfs_dir_entry *dent;
    char *dent_pos, *dent_end_pos, *dir_data=NULL;
    size_t dir_size=0;
    loff_t dir_offset=0;

    if (DEBUG) printk("vvsfs - lookup\nFile name: %s\n",
                      dentry->d_name.name);
    //Read content.
    if(vvsfs_file_read_content(dir, &dir_data, &dir_size, &dir_offset, false)<0) {
        if(dir_data) {
            kfree(dir_data);
        }
        printk("No enough memory for block in readdir.");
        return ERR_PTR(-EINVAL);
    }
    //Check dir size.
    if(dir_data!=NULL && dir_size>0) {
        //Start to search in the dentry list.
        dent_end_pos=dir_data+dir_size;
        for (dent_pos=dir_data;
             dent_pos<dent_end_pos;
             dent_pos += vvsfs_dentry_size(dent)) {
            dent = (struct vvsfs_dir_entry *) dent_pos;
            if ((dent->name_size == dentry->d_name.len) &&
                    strncmp(dent->name,dentry->d_name.name,dentry->d_name.len) == 0) {
                inode = vvsfs_iget(dir->i_sb, dent->inode_number);

                if (!inode) {
                    kfree(dir_data);
                    return ERR_PTR(-EACCES);
                }
                kfree(dir_data);
                d_add(dentry, inode);
                return NULL;

            }
        }
        kfree(dir_data);
    }
    d_add(dentry, inode);
    printk("Find no result\nvvsfs - lookup done.\n");
    return NULL;
}

// vvsfs_add_dentry - add one dentry to the vvsfs block.
static int vvsfs_add_dentry(struct inode *dir, struct dentry *dentry,
                            struct inode *inode) {
    struct vvsfs_dir_entry *dent;
    struct vvsfs_inode dir_inode;
    int dent_size;
    loff_t dent_offset;

    if(DEBUG) printk("vvsfs - add dentry\n");
    //Allocate the content.
    dent_size=sizeof(struct vvsfs_dir_entry)+dentry->d_name.len*sizeof(char);
    dent=(struct vvsfs_dir_entry *)kmalloc(dent_size, GFP_KERNEL);
    //Set content to the new dentry.
    strncpy(dent->name, dentry->d_name.name,dentry->d_name.len);
    dent->name_size=dentry->d_name.len;
    dent->inode_number = inode->i_ino;
    //Get dir inode.
    vvsfs_get_inode(dir, &dir_inode);
    //Offset is the end of the content.
    dent_offset=dir_inode.size;
    if(DEBUG) printk("Written position: %Ld\n", (long long)dent_offset);
    //Write data to the file content.
    vvsfs_file_write_content(dir, 0, (char *)dent,
                             dent_size, &dent_offset, false);
    //Mark as dirty inode.
    mark_inode_dirty(dir);
    d_instantiate(dentry, inode);
    return 0;
}

// vvsfs_empty_inode - finds the first free inode (returns -1 is unable to find one)
static int vvsfs_empty_inode(struct super_block *sb,
                             int block_id,
                             struct inode *inode) {
    struct vvsfs_block *block=NULL;
    struct vvsfs_mounted_info *mounted_info=
            (struct vvsfs_mounted_info *)sb->s_fs_info;
    struct vvsfs_inode *file_inode=NULL;
    int i, j, inode_max_block, inode_id=0,
            max_inode_count=mounted_info->device_info.block_size /
            sizeof(struct vvsfs_inode);

    //Allocate block.
    block=vvsfs_alloc_block(sb);
    if(block==NULL) {
        printk("no memory for block in empty inode.");
        return -1;
    }
    //Get the size.
    inode_max_block=mounted_info->device_info.inode_block_count +
            BLOCK_INODE_LIST;
    //Find one inode number.
    for(i=BLOCK_INODE_LIST; i<inode_max_block; ++i) {
        //Read the block.
        vvsfs_readrawblock(sb, i, block);
        //Set the initial inode.
        file_inode=(struct vvsfs_inode *)block;
        //Check the inode.
        for(j=0; j<max_inode_count; ++j) {
            if(!(file_inode->flag & VVSFS_IFLAG_ISUSED)) {
                //Find the inode.
                file_inode->flag |= VVSFS_IFLAG_ISUSED;
                file_inode->block=block_id;
                file_inode->count=1;
                file_inode->mode=inode->i_mode;
                file_inode->uid=inode->i_uid.val;
                file_inode->gid=inode->i_gid.val;
                file_inode->time_create=inode->i_ctime.tv_sec;
                file_inode->time_modify=inode->i_mtime.tv_sec;
                file_inode->time_access=inode->i_atime.tv_sec;
                file_inode->size=0;
                file_inode->c_size=0;
                //Write the block.
                vvsfs_writerawblock(sb, i, block);
                //Free the block.
                vvsfs_free_block(&block);
                return inode_id;
            }
            ++inode_id;
            ++file_inode;
        }
    }
    //Free the block.
    vvsfs_free_block(&block);
    //Error.
    return -1;
}

//vvsfs_link - create hard link
static int vvsfs_link(struct dentry *old_dentry,
                      struct inode *dir,struct dentry *dentry) {
    struct inode *inode = old_dentry->d_inode;
    struct vvsfs_inode file_inode;

    dquot_initialize(dir);

    if (DEBUG) printk("vvsfs - link\n%s -> %s\n",
                      old_dentry->d_name.name,
                      dentry->d_name.name);
    //Add new dentry to directory.
    if(vvsfs_add_dentry(dir, dentry, inode)<0) {
        return -ENOMEM;
    }
    //Update the VFS inode.
    inode->i_ctime = CURRENT_TIME_SEC;
    inode_inc_link_count(inode);
    ihold(inode);
    //Get the inode to file inode.
    vvsfs_get_inode(inode, &file_inode);
    //Increase the file inode reference.
    ++file_inode.count;
    file_inode.time_create=inode->i_ctime.tv_sec;
    //Write back the file_inode.
    vvsfs_set_inode(inode, &file_inode);
    //Mark inode dirty.
    mark_inode_dirty(inode);
    return 0;
}

// vvsfs_symlink - create symbol link.
static int vvsfs_symlink (struct inode *dir,struct dentry *dentry,
                          const char *symname){
    int name_len=strlen(symname);
    char *file_content=NULL;
    struct inode *inode;
    loff_t ppos=0;

    if(DEBUG) printk("vvsfs - symlink:\n%s <- %s\n", symname, dentry->d_name.name);
    if (!dir) return -1;

    dquot_initialize(dir);

    //Generate the content.
    file_content=(char *)kmalloc(name_len+1, GFP_KERNEL);
    if(file_content==NULL) {
        return -ENOMEM;
    }
    //Copy the file content to file content.
    memcpy(file_content, symname, name_len);
    file_content[name_len]='\0';
    //Generate inode.
    inode=vvsfs_new_inode(dir, S_IFLNK | S_IRWXUGO);
    if (!inode){
        kfree(file_content);
        return -ENOMEM;
    }
    //Configure the inode.
    vvsfs_set_operations(inode);
    inode->i_blocks = 1;

    //Write content to the file.
    vvsfs_file_write_content(inode,
                             inode->i_mode,
                             file_content,
                             name_len+1,
                             &ppos,
                             false);
    //Free the content.
    kfree(file_content);
    //Update inode info.
    inode->i_size=name_len;

    if(vvsfs_add_dentry(dir, dentry, inode)<0){
        return -1;
    }
    mark_inode_dirty(inode);
    printk("Symlink created %ld\n",inode->i_ino);
    return 0;
}

static int vvsfs_block_inc_link_count(struct super_block *sb, int block_no) {
    struct vvsfs_block *filedata=NULL;

    //Clear the block content.
    filedata=vvsfs_alloc_block(sb);
    if(filedata==NULL) {
        return -ENOMEM;
    }
    //Read and update the flag to empty.
    vvsfs_readrawblock(sb, block_no, filedata);
    ++filedata->count;
    vvsfs_writerawblock(sb, block_no, filedata);
    //Free file data.
    vvsfs_free_block(&filedata);
    return 0;
}

static int vvsfs_block_dec_link_count(struct super_block *sb, int block_no) {
    struct vvsfs_block *blockdata=NULL;
    struct vvsfs_device_block *device_info;
    int i, before_root;

    //Clear the block content.
    blockdata=vvsfs_alloc_block(sb);
    if(blockdata==NULL) {
        return -ENOMEM;
    }
    printk("Reduce ref on block %d\n", block_no);
    //Read and update the flag to empty.
    vvsfs_readrawblock(sb, block_no, blockdata);
    --blockdata->count;
    if(blockdata->count==0) {
        blockdata->flag |= VVSFS_FLAG_ISEMPTY;
    }
    //Write the data back.
    vvsfs_writerawblock(sb, block_no, blockdata);
    //Check file data block.
    if(blockdata->count==0){
        //Check the device block.
        device_info = &(((struct vvsfs_mounted_info *)
                        sb->s_fs_info)->device_info);
        if(block_no == device_info->last_used_block) {
            //Find the last unused block.
            for(i=block_no,
                before_root=device_info->inode_block_count+BLOCK_INODE_LIST-1;
                i>before_root;
                --i) {
                vvsfs_readrawblock(sb, i, blockdata);
                if(!(blockdata->flag & VVSFS_FLAG_ISEMPTY)) {
                    //Save the new last used block.
                    device_info->last_used_block = i;
                    vvsfs_update_device_info(sb);
                    break;
                }
            }
        }
    }
    //Free file data.
    vvsfs_free_block(&blockdata);
    return 0;
}

// vvsfs_unlink - remove the inode and dentry from dir.
static int vvsfs_unlink(struct inode *dir, struct dentry *dentry) {
    struct vvsfs_dir_entry *dent = NULL;
    struct inode *inode = NULL;
    struct vvsfs_inode dir_inode, file_inode;
    char *dent_pos, *dent_end_pos, *dent_next_pos, *dir_data=NULL;
    int dent_size, dir_size=0;
    loff_t dir_offset=0;

    if (DEBUG) printk("vvsfs - unlink\n");
    //Read directory size.
    if(vvsfs_file_read_content(dir,
                               &dir_data, &dir_size, &dir_offset, false)<0) {
        if(dir_data) {
            //Free the dir data.
            kfree(dir_data);
        }
        return -EINVAL;
    }
    //Get the directory data.
    vvsfs_get_inode(dir, &dir_inode);
    //Start to search in the dirdata.
    for (dent_pos=dir_data;
         ((dent_end_pos)=(dir_data+(dir_inode.size)));
         dent_pos+=vvsfs_dentry_size(dent)) {
        //Update the dentry.
        dent = (struct vvsfs_dir_entry *)dent_pos;
        if ((dent->name_size == dentry->d_name.len) &&
                        strncmp(dent->name,dentry->d_name.name,dentry->d_name.len) == 0) {
            //Get the inode of the file.
            inode = vvsfs_iget(dir->i_sb, dent->inode_number);
            if (!inode) {
                kfree(dir_data);
                return -EACCES;
            }
            //Check the inode is directory or not.
            if(S_ISDIR(inode->i_mode) && inode->i_size > 0) {
                //Directory is not empty.
                kfree(dir_data);
                return -ENOTEMPTY;
            }
            //Update VFS inode information.
            inode->i_ctime=dir->i_ctime;
            inode_dec_link_count(inode);
            mark_inode_dirty(inode);
            //Update VVSFS inode information.
            vvsfs_get_inode(inode, &file_inode);
            file_inode.count=inode->i_nlink;
            //Check inode count.
            if(inode->i_nlink==0) {
                vvsfs_block_dec_link_count(inode->i_sb, file_inode.block);
                //Update file inode.
                file_inode.flag &= ~VVSFS_IFLAG_ISUSED;
            }
            //Update the vvsfs inode data.
            vvsfs_set_inode(inode, &file_inode);
            //Update dentry data in the directory.
            dent_size=vvsfs_dentry_size(dent);
            dent_next_pos=dent_pos+dent_size;
            //Overwrite the content.
            memcpy(dent_pos, dent_next_pos, dent_end_pos-dent_next_pos);
            //Update the size.
            dir_size-=dent_size;
            dir_inode.size=dir_size;
            //Update dir inode.
            vvsfs_set_inode(dir, &dir_inode);
            //Write file content.
            dir_offset=0;
            vvsfs_file_write_content(dir, 0,
                                     dir_data, dir_size,
                                     &dir_offset, false);
            //Free the content.
            kfree(dir_data);
            return 0;
        }
    }
    //Error happenes.
    kfree(dir_data);
    //Can't find it.
    return -ENOENT;
}

static int vvsfs_rename(struct inode * old_dir, struct dentry * old_dentry,
                        struct inode * new_dir, struct dentry * new_dentry ) {
    struct inode *old_inode=old_dentry->d_inode;
    struct inode *inode;
    int err;
    struct vvsfs_inode file_inode, new_file_inode;
    char *file_content=NULL;
    int file_content_size=0;
    loff_t file_offset=0;

    if (DEBUG) printk("vvsfs - rename\n");
    //Check directory pointer first.
    if (!old_dir) return -1;
    if (!new_dir) return -1;
    //Read the raw inode data.
    vvsfs_get_inode(old_inode, &file_inode);
    //Read the file content.
    if(vvsfs_file_read_content(old_inode, &file_content, &file_content_size,
                               &file_offset, false)<0) {
        if(!file_content) {
            kfree(file_content);
        }
        return -ENOMEM;
    }
    //Unlink the original file.
    if(vvsfs_unlink(old_dir, old_dentry)<0){
        kfree(file_content);
        return -EINVAL;
    }
    //Create a new inode.
    inode = vvsfs_new_inode(new_dir, file_inode.mode);
    if (!inode) {
        kfree(file_content);
        return -ENOSPC;
    }
    vvsfs_set_operations(inode);
    inode->i_blocks = 1;
    //Add inode to directory.
    if(vvsfs_add_dentry(new_dir, new_dentry, inode)<0) {
        kfree(file_content);
        return -ENOSPC;
    }
    //Write content to the target file.
    file_offset=0;
    err=vvsfs_file_write_content(inode, 0, file_content, file_content_size,
                                 &file_offset, false);
    kfree(file_content);
    if(err<0) {
        return err;
    }
    //Update all the inode data.
    vvsfs_get_inode(inode, &new_file_inode);
    new_file_inode.gid=file_inode.gid;
    new_file_inode.uid=file_inode.uid;
    new_file_inode.mode=file_inode.mode;
    vvsfs_set_inode(inode, &new_file_inode);
    return 0;
}

// vvsfs_empty_block - finds the first free block (returns -1 is unable to find one)
static int vvsfs_empty_block(struct super_block *sb,
                             bool enable_ref) {
    struct vvsfs_block *block=NULL;
    struct vvsfs_mounted_info *mounted_info=
            (struct vvsfs_mounted_info *)sb->s_fs_info;
    int k, root_block, max_block;

    //Allocate block.
    block=vvsfs_alloc_block(sb);
    if(block==NULL) {
        printk("no memory for block in empty block.");
        return -1;
    }
    //Get the size.
    root_block=mounted_info->device_info.inode_block_count + BLOCK_INODE_LIST;
    max_block=mounted_info->device_info.block_count;
    for(k=(mounted_info->flag & VVSFS_MOUNT_FLAG_SSDWRITE)?
            (mounted_info->ssd_start_empty_block++):
            (root_block+1);
        k<max_block; ++k) {
        //Read the raw block
        vvsfs_readrawblock(sb, k, block);
        if (block->flag & VVSFS_FLAG_ISEMPTY ||
                (enable_ref && block->size==0)) {
            if(DEBUG) printk("Find empty block: %d\n", k);
            //Compare with ssd block size.
            if(k > mounted_info->ssd_start_empty_block) {
                //Update empty block.
                mounted_info->ssd_start_empty_block=k+1;
            }
            //Check ssd block position.
            if(mounted_info->ssd_start_empty_block
                    >=mounted_info->device_info.block_size) {
                //Reset back to beginning.
                mounted_info->ssd_start_empty_block=root_block+1;
            }
            //Free the block.
            vvsfs_free_block(&block);
            return k;
        }
    }
    //Check ssd block position.
    if(mounted_info->ssd_start_empty_block
            >=mounted_info->device_info.block_size) {
        //Reset back to beginning.
        mounted_info->ssd_start_empty_block=root_block+1;
    }
    vvsfs_free_block(&block);
    return -1;
}

static void vvsfs_update_device_info(struct super_block * sb) {
    struct vvsfs_mounted_info *mounted_info=
            (struct vvsfs_mounted_info *)sb->s_fs_info;
    //Write content to device block.
    vvsfs_writerawblock(sb, BLOCK_DEVICE, &mounted_info->device_info);
}

// vvsfs_new_inode - find and construct a new inode.
struct inode *vvsfs_new_inode(const struct inode * dir, umode_t mode)
{
    struct vvsfs_block *block;
    struct super_block * sb;
    struct inode *inode;
    struct vvsfs_device_block *device_info;
    struct vvsfs_mounted_info *mounted_info;
    int new_block_number, new_inode_number;

    if (DEBUG) printk("vvsfs - new inode\n");

    if (!dir) return NULL;
    sb = dir->i_sb;
    mounted_info=(struct vvsfs_mounted_info *)sb->s_fs_info;
    device_info = &(mounted_info->device_info);
    //Allocate block.
    block=vvsfs_alloc_block(sb);
    if(block==NULL) {
        printk("vvsfs - no enough memory for new inode.\n");
        return NULL;
    }

    /* get an vfs inode */
    inode = new_inode(sb);
    if (!inode) {
        vvsfs_free_block(&block);
        return NULL;
    }

    /* find a spare inode in the vvsfs */
    new_block_number = vvsfs_empty_block(sb, true);
    if (new_block_number == -1) {
        printk("vvsfs - block is all used.\n");
        vvsfs_free_block(&block);
        return NULL;
    }
    //Check new block number is greater than the device block.
    if (new_block_number > device_info->last_used_block) {
        //Set the new last used block.
        device_info->last_used_block=new_block_number;
        //Update the device block.
        vvsfs_update_device_info(sb);
    }
    //Initial the inode owner mode.
    inode_init_owner(inode, dir, mode);
    inode->i_ctime = inode->i_mtime = inode->i_atime = CURRENT_TIME;
    new_inode_number = vvsfs_empty_inode(sb,
                                         new_block_number,
                                         inode);
    if (new_inode_number == -1) {
        printk("vvsfs - inode table is full.\n");
        vvsfs_free_block(&block);
        return NULL;
    }

    //Initial the block data here.
    vvsfs_readblock(sb, new_block_number, block);
    memset(block->data, 0, mounted_info->block_content_size);
    block->flag = 0;
    block->size = 0;
    block->hash = 0;
    ++block->count;
    printk("Block count: %d\n", block->count);
    //Write the block.
    vvsfs_writeblock(sb, new_block_number, block);
    //Free the block.
    vvsfs_free_block(&block);
    //Set the inode data.
    inode->i_ino = new_inode_number;
    inode->i_op = NULL;
    inode->i_fop = NULL;
    insert_inode_hash(inode);

    return inode;
}

// vvsfs_create - create a new file in a directory 
static int
vvsfs_create(struct inode *dir, struct dentry* dentry, umode_t mode, bool excl)
{
    struct inode * inode;

    if (DEBUG) printk("vvsfs - create : %s\n",dentry->d_name.name);
    //Check directory pointer first.
    if (!dir) return -1;

    //Create a new inode.
    inode = vvsfs_new_inode(dir, S_IRUGO|S_IWUGO|S_IFREG|mode);
    if (!inode) {
        return -ENOSPC;
    }
    vvsfs_set_operations(inode);
    inode->i_blocks = 1;
    //Add inode to directory.
    if(vvsfs_add_dentry(dir, dentry, inode)<0) {
        return -ENOSPC;
    }
    printk("File created %ld\n",inode->i_ino);
    return 0;
}

// vvsfs_mkdir - create a new directory.
static int vvsfs_mkdir(struct inode *dir,struct dentry *dentry, umode_t mode)
{
    struct inode *inode;

    if (DEBUG) printk("vvsfs - mkdir: %s\n",dentry->d_name.name);
    /* get an vfs inode */
    if (!dir) return -1;

    //Create a new inode.
    inode = vvsfs_new_inode(dir, mode|S_IFDIR);
    if (!inode) {
        return -ENOSPC;
    }
    vvsfs_set_operations(inode);
    //Add dentry to directory.
    if(vvsfs_add_dentry(dir, dentry, inode)<0) {
        return -1;
    }
    printk("Directory created %ld\n",inode->i_ino);
    return 0;
}

// vvsfs_follow_link - follow the link
static void *vvsfs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
    //Get inode.
    struct inode *inode=dentry->d_inode;
    char *link_filename=NULL;
    size_t link_filename_size=0;
    loff_t ppos=0;
    int ret=0;

    if(DEBUG) printk("vvsfs - follow link: %s\n", dentry->d_name.name);

    //Read file content from the inode file.
    ret=vvsfs_file_read_content(inode,
                                &link_filename,
                                &link_filename_size,
                                &ppos,
                                false);
    printk("link filename size: %d\n", link_filename_size);
    if(ret <= 0) {
        //Check the file name pointer.
        if(link_filename!=NULL){
            //Free the memory.
            kfree(link_filename);
            //Return the error.
            return ERR_PTR(ret);
        }
    }
    //Already get the filename.
    printk("link to file: %s\n", link_filename);
    nd_set_link(nd, link_filename);
    return NULL;
}

// vvsfs_find_writeblock - before writing a file, try to find a file which has already contains the same data, write the data to the block.
static int
vvsfs_find_write_block(struct super_block *sb,
                       struct inode *inode,
                       struct vvsfs_block *filedata,
                       struct vvsfs_inode *file_inode) {
    int target_block=-1;

    //Update block hash.
    filedata->hash=vvsfs_block_hash(filedata);
    //Find the block which has the same data.
    target_block=vvsfs_find_block(sb, filedata->hash, filedata->flag);
    if(target_block==-1){
        //Reset the new block index.
        target_block=file_inode->block;
        //Check block reference status.
        if(filedata->count>1) {
            //Reduce the original block.
            vvsfs_block_dec_link_count(sb, target_block);
            //Get a new block no.
            target_block=vvsfs_empty_block(sb, false);
            //Check block.
            if(target_block==-1) {
                return -ENOSPC;
            }
            //Reset the count to 1.
            filedata->count=1;
            //Update inode.
            file_inode->block=target_block;
        }
        //Update the inode data.
        vvsfs_set_inode(inode, file_inode);
        //Write the block to target position.
        vvsfs_writeblock(sb, target_block, filedata);
    } else {
        //Check is the thing the same or not.
        //If the block is exactly what we have already set, which means the data
        //is not changed. Ignore the written request.
        //Fixed: We still need to rewrite it for updating the new state.
        if(target_block==file_inode->block) {
            //Becuase the flag is updated.
            vvsfs_writeblock(sb, target_block, filedata);
        } else {
            //Reduce the block ref count.
            vvsfs_block_dec_link_count(sb, file_inode->block);
            //Write the content.
            file_inode->block=target_block;
            vvsfs_set_inode(inode, file_inode);
            //Increase the new block ref count.
            vvsfs_block_inc_link_count(sb, file_inode->block);
        }
    }
    return 0;
}

static int vvsfs_mknod(struct inode *dir, struct dentry *dentry,
                       umode_t mode, dev_t rdev) {
    struct inode *inode;
    struct vvsfs_inode file_inode;

    if(DEBUG) printk("vvsfs - mknod\n");
    if (!new_valid_dev(rdev))
        return -EINVAL;
    printk("%ld\n", (long)rdev);
    dquot_initialize(dir);
    //Create inodes.
    inode=vvsfs_new_inode(dir, mode);
    if(inode==NULL) {
        printk("vvsfs - cannot get inode\n");
        return -EINVAL;
    }
    //Initial the device inode.
    init_special_inode(inode, inode->i_mode, rdev);
    vvsfs_set_operations(inode);
    vvsfs_add_dentry(dir, dentry, inode);
    //Set the inode size as rdev.
    vvsfs_get_inode(inode, &file_inode);
    file_inode.c_size=rdev;
    vvsfs_set_inode(inode, &file_inode);
    //Mark them as dirty inode.
    mark_inode_dirty(dir);
    mark_inode_dirty(inode);
    if(DEBUG) printk("vvsfs - mknod done\n");
    return 0;
}

static int vvsfs_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) {
    struct page *page;
    struct vvsfs_file_private *f_priv;
    printk("vvsfs - mmap fault\n");
    f_priv=(struct vvsfs_file_private *)vma->vm_private_data;
    if(!f_priv->map_data) {
        //No data.
        return 0;
    }

    page=virt_to_page(f_priv->map_data);

    get_page(page);
    vmf->page=page;
    printk("vvsfs - mmap fault done.\n");
    return 0;
}

static struct vm_operations_struct vvsfs_mmap_vm_ops = {
    .fault = vvsfs_mmap_fault,
};

// vvsfs_file_mmap - map a file to memory.
static int vvsfs_file_mmap (struct file *filp, struct vm_area_struct *vma){
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
    struct inode *inode = filp->f_dentry->d_inode;
#else
    struct inode *inode = filp->f_path.dentry->d_inode;
#endif
    struct vvsfs_file_private *f_priv=NULL;
    size_t page_size=PAGE_SIZE;
    loff_t offset=0;

    printk("vvsfs - mmap\nfile name: %s\n", filp->f_path.dentry->d_name.name);
    vma->vm_ops = &vvsfs_mmap_vm_ops;
    vma->vm_flags |= VM_IO;
    vma->vm_private_data=filp->private_data;
    f_priv=(struct vvsfs_file_private *)vma->vm_private_data;
    f_priv->map_data=(char *)get_zeroed_page(GFP_KERNEL);
    //Copy file data to target data.
    vvsfs_file_read_content(inode, &f_priv->map_data, &page_size, &offset, false);
    printk("vvsfs - mmap done.\n");
    return 0;
}

// vvsfs_file_open - do additional operations when open the file.
static int vvsfs_file_open(struct inode *inode, struct file *filp) {
    struct vvsfs_file_private *f_priv=(struct vvsfs_file_private *)
            kmalloc(sizeof(struct vvsfs_file_private), GFP_KERNEL);
    printk("vvsfs - open\n");

    f_priv->map_data = NULL;
    filp->private_data=f_priv;
    printk("vvsfs - open done.\n");
    return 0;
}

// vvsfs_file_close - do additional operations when close the file.
static int vvsfs_file_close(struct inode *inode, struct file *filp) {
    struct vvsfs_file_private *f_priv=filp->private_data;
    struct vvsfs_inode file_inode;
    loff_t offset=0;

    printk("vvsfs - close\n");

    //Free map data.
    if(f_priv->map_data) {
        //Get the file inode.
        vvsfs_get_inode(inode, &file_inode);
        //Reset offset.
        offset=0;
        //Copy the data to the inode.
        vvsfs_file_write_content(inode,
                                 0,
                                 f_priv->map_data,
                                 MIN(file_inode.size, PAGE_SIZE),
                                 &offset,
                                 false);
        //Free the page.
        free_page((unsigned long)f_priv->map_data);
    }
    //Free the map data.
    kfree(f_priv);
    filp->private_data=NULL;
    printk("vvsfs - close done.\n");
    return 0;
}


// vvsfs_setattr - change attribute of one file.
static int vvsfs_setattr (struct dentry *dentry, struct iattr *iattr)
{
    //Get inode.
    struct inode *inode=dentry->d_inode;
    struct vvsfs_mounted_info *mounted_info;
    char *filedata=NULL;
    size_t filelength=0, read_length;
    loff_t ppos=0;
    struct vvsfs_inode file_inode;
    int ret=0;

    if(DEBUG) printk("vvsfs - setattr: %s\n", dentry->d_name.name);
    //Check inode.
    if (!inode) {
        printk("vvsfs - Problem with file inode\n");
        return -EINVAL;
    }
    //Start the journal.
    vvsfs_start_journal(JOURNAL_OP_SETATTR, inode);

    printk("Attribute Flag: %d\n", iattr->ia_valid);
    //Check validation.
    if(iattr->ia_valid)
    {
        //Read out the inode information.
        vvsfs_get_inode(inode, &file_inode);
        if(iattr->ia_valid & ATTR_SIZE)
        {
            //Get mounted info, there stores the content size.
            mounted_info=(struct vvsfs_mounted_info *)inode->i_sb->s_fs_info;
            //Calcualte the file length.
            filelength=MIN(iattr->ia_size, mounted_info->block_content_size);
            printk("File size: %ld\n", (long)filelength);
            //Check the file length.
            if(filelength>0) {
                //Allocate the memory for the file.
                filedata=(char *)kmalloc(filelength, GFP_KERNEL);
                //Check file data.
                if(filedata==NULL) {
                    return -ENOMEM;
                }
                //Clear the memory data.
                memset(filedata, 0, filelength);
                //Read the file content.
                read_length=MIN(filelength, file_inode.size);
                ret=vvsfs_file_read_content(inode, &filedata,
                                            &read_length,
                                            &ppos, false);
                if(ret<0) {
                    kfree(filedata);
                    return ret;
                }
            }
            //Reset the ppos.
            ppos=0;
            //Write the file content back.
            ret=vvsfs_file_write_content(inode, 0, filedata,
                                         filelength, &ppos, false);
            //Free the file data.
            kfree(filedata);
            //Check the result of write file.
            if(ret<0) {
                return ret;
            }
            //Update the file inode, because it might changed the block.
            vvsfs_get_inode(inode, &file_inode);
            //Update the file length.
            inode->i_size=filelength;
            file_inode.size=filelength;
        }
        if(iattr->ia_valid & ATTR_MODE)
        {
            if(DEBUG) printk("Update file mode to %d\n", iattr->ia_mode);
            file_inode.mode=iattr->ia_mode;
            inode->i_mode=iattr->ia_mode;
        }
        if(iattr->ia_valid & ATTR_UID)
        {
            if(DEBUG) printk("Update owner user to %d\n", iattr->ia_uid.val);
            file_inode.uid=iattr->ia_uid.val;
            inode->i_uid=iattr->ia_uid;
        }
        if(iattr->ia_valid & ATTR_GID)
        {
            if(DEBUG) printk("Update owner group to %d\n", iattr->ia_gid.val);
            file_inode.gid=iattr->ia_gid.val;
            inode->i_gid=iattr->ia_gid;
        }
        if(iattr->ia_valid & ATTR_ATIME)
        {
            if(DEBUG) printk("Update access time\n");
            file_inode.time_access = iattr->ia_atime.tv_sec;
            inode->i_atime = iattr->ia_atime;
        }
        if(iattr->ia_valid & ATTR_MTIME)
        {
            if(DEBUG) printk("Update modify time\n");
            file_inode.time_modify = iattr->ia_mtime.tv_sec;
            inode->i_mtime = iattr->ia_mtime;
        }
        if(iattr->ia_valid & ATTR_CTIME)
        {
            if(DEBUG) printk("Update create time\n");
            file_inode.time_create = iattr->ia_ctime.tv_sec;
            inode->i_ctime = iattr->ia_ctime;
        }
        //Set back the inode information.
        vvsfs_set_inode(inode, &file_inode);
    }
    //Start the journal.
    vvsfs_set_journal_state(JOURNAL_NONE, inode->i_sb);
    //Mark dirty inode.
    setattr_copy(inode, iattr);
    mark_inode_dirty(inode);
    if(DEBUG) printk("vvsfs - setattr done.\n");
    return 0;
}

static void vvsfs_file_write_block(struct super_block *sb,
                                   struct inode *inode,
                                   struct vvsfs_inode *file_inode,
                                   struct vvsfs_block *block_data,
                                   const char *buf,
                                   size_t count,
                                   loff_t ppos) {
    //Read the raw block data.
    vvsfs_readblock(sb, file_inode->block, block_data);
    //Update the block data size.
    block_data->size=ppos+count;
    //Check the block size.
    if(block_data->size>0) {
        //Update the block data.
        memcpy(block_data->data + ppos, buf, count);
    }
    //Write the block data back.
    vvsfs_find_write_block(sb, inode, block_data, file_inode);
}

// vvsfs_file_write - write to a file
static ssize_t
vvsfs_file_write_content(struct inode *inode, int file_flags, const char *buf, size_t count, loff_t *ppos, bool from_user)
{
    struct vvsfs_block *block_data=NULL;
    ssize_t pos;
    size_t target_size, compress_size=0;
    loff_t offset=0;
    struct super_block *sb;
    struct vvsfs_inode file_inode;
    struct vvsfs_mounted_info *mounted_info;
    char *file_buffer=NULL, *compress_buffer=NULL, *content_buffer=NULL;
    int ret, content_size=0;

    if (DEBUG) printk("vvsfs - file write content - count : %zu ppos %Ld user : %d\n",count, *ppos, from_user);

    if (!inode) {
        return -EINVAL;
    }

    if (*ppos > inode->i_size || count < 0) {
        printk("vvsfs - attempting to write over the end of a file.\n");
        return 0;
    }

    sb = inode->i_sb;
    mounted_info=(struct vvsfs_mounted_info *)sb->s_fs_info;
    if (file_flags & O_APPEND)
        pos = inode->i_size;
    else
        pos = *ppos;

    target_size=pos+count;
    if (target_size > mounted_info->block_content_size) {
        return -ENOSPC;
    }

    //Allocate the block data.
    block_data=vvsfs_alloc_block(sb);
    if(block_data==NULL) {
        printk("vvsfs - no memory for caching to write.\n");
        kfree(file_buffer);
        return -ENOMEM;
    }
    //Read block data.
    vvsfs_get_inode(inode, &file_inode);
    //Update the file inode data.
    file_inode.size = target_size;
    //Check the compress size.
    file_inode.c_size = -1;
    //Release the compression flag.
    file_inode.flag &= ~VVSFS_IFLAG_COMPRESS;
    //Write the buffered content to kernel buffer.
    if(file_inode.size>0) {
        //Create kernel buffer.
        file_buffer=(char *)kmalloc(target_size, GFP_KERNEL);
        if(file_buffer==NULL) {
            vvsfs_free_block(&block_data);
            return -ENOMEM;
        }
        //Set the target buffer.
        content_buffer=file_buffer;
        content_size=target_size;
        //Read the original content to the file, if it has.
        if(file_inode.size>0) {
            ret=vvsfs_file_read_content(inode,
                                        &file_buffer,
                                        &file_inode.size,
                                        &offset,
                                        false);
            if(ret<0) {
                kfree(file_buffer);
                vvsfs_free_block(&block_data);
                return ret;
            }
        }

        //Write the content to the file buffer.
        if(from_user){
            if (copy_from_user(file_buffer+pos, buf, count)) {
                kfree(file_buffer);
                vvsfs_free_block(&block_data);
                return -ENOSPC;
            }
        } else {
            memcpy(file_buffer+pos, buf, count);
        }

        //Check the compress flag.
        if(mounted_info->flag & VVSFS_MOUNT_FLAG_COMPRESS) {
            //Calculate the size.
            compress_size=lz4_compressbound(target_size);
            //Compress the content.
            compress_buffer=(char *)kmalloc(compress_size, GFP_KERNEL);
            //Check buffer.
            if(compress_buffer==NULL) {
                kfree(file_buffer);
                vvsfs_free_block(&block_data);
                return -ENOMEM;
            }
            //Compress the data.
            lz4_compress(file_buffer, target_size,
                         compress_buffer, &compress_size,
                         mounted_info->compress_cache);
            //Check the comrpession size.
            if(compress_size < file_inode.size) {
                //Set the file inode data.
                file_inode.c_size=compress_size;
                file_inode.flag |= VVSFS_IFLAG_COMPRESS;
                content_buffer=compress_buffer;
                content_size=compress_size;
                //Free the file content.
                kfree(file_buffer);
                file_buffer=NULL;
            } else {
                //Free the compress buffer.
                kfree(compress_buffer);
                compress_buffer=NULL;
            }
        }
    }

    //Write the content to block.
    vvsfs_file_write_block(sb, inode, &file_inode, block_data,
                           content_buffer, content_size, 0);
    //Move the ppos.
    *ppos = pos;
    //Update the file inode.
    inode->i_size=file_inode.size;
    inode->i_blocks = 1;
    //Recover the buffer.
    if(compress_buffer) {
        kfree(compress_buffer);
        compress_buffer=NULL;
    }
    if(file_buffer) {
        kfree(file_buffer);
        file_buffer=NULL;
    }
    //Free the buffer.
    vvsfs_free_block(&block_data);
    //Update the file inode.
    if (DEBUG) printk("vvsfs - file write content done : %zu ppos %Ld\n",count,*ppos);

    return count;
}

// vvsfs_file_write - write to a file
static ssize_t
vvsfs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
    struct inode *inode = filp->f_dentry->d_inode;
#else
    struct inode *inode = filp->f_path.dentry->d_inode;
#endif
    ssize_t ret;

    if(DEBUG) printk("vvsfs - file write\n");

    if (!inode) {
        printk("vvsfs - Problem with file inode\n");
        return -EINVAL;
    }

    if (!(S_ISREG(inode->i_mode))) {
        printk("vvsfs - not regular file\n");
        return -EINVAL;
    }

    //Start journal.
    vvsfs_start_journal(JOURNAL_OP_WRITEFILE, inode);
    //Get the file write.
    ret=vvsfs_file_write_content(inode, filp->f_flags, buf, count, ppos, true);
    //End journal.
    vvsfs_set_journal_state(JOURNAL_NONE, inode->i_sb);
    return ret;
}

// vvsfs_file_read - read data from a file to a memory part.
static ssize_t
vvsfs_file_read_content(struct inode *inode, char **buf, size_t *count, loff_t *ppos, bool to_user)
{
    char                    *start;
    ssize_t                  offset, size, allocate_size;
    struct vvsfs_block *filedata;
    struct vvsfs_inode file_inode;
    struct super_block * sb;
    char *decompress_cache=NULL, *read_src=NULL;

    if (DEBUG) printk("vvsfs - file read content - count : %zu ppos %Ld\n",*count,*ppos);

    if (!inode) {
        return -EINVAL;
    }
    if ((*buf!=NULL) && (*ppos > inode->i_size || (*count) <= 0)) {
        printk("vvsfs - attempting to read from the end of a file.\n");
        return 0;
    }
    //Set the super block.
    sb = inode->i_sb;
    //Allocate the file data.
    filedata=vvsfs_alloc_block(sb);
    if(filedata==NULL) {
        printk("vvsfs - no enough memory for read\n");
        return -ENOMEM;
    }

    printk("r : readblock\n");
    vvsfs_get_inode(inode, &file_inode);
    if(vvsfs_readblock(sb, file_inode.block, filedata)==0) {
        vvsfs_free_block(&filedata);
        return -EACCES;
    }

    //Check buf pointer.
    if((*buf)==NULL) {
        //Calcualte the size they wants to read.
        allocate_size=file_inode.size - (*ppos);
        if(allocate_size==0) {
            //Free the block
            vvsfs_free_block(&filedata);
            return 0;
        }
        //Allocate a new memory to the buf.
        (*buf)=(char *)kmalloc(allocate_size, GFP_KERNEL);
        if((*buf)==NULL) {
            //Free the block
            vvsfs_free_block(&filedata);
            return -ENOMEM;
        }
        //Give the count the actual size.
        (*count)=allocate_size;
        printk("Create new kernel memory part, size: %d, file size: %d\n", (*count), file_inode.size);
        //Force is user to false.
        to_user=false;
    }

    //Check compression flag.
    if(file_inode.flag & VVSFS_IFLAG_COMPRESS) {
        //Allocate decompress part.
        decompress_cache=(char *)kmalloc(file_inode.size, GFP_KERNEL);
        //Check decompress state.
        if(decompress_cache==NULL) {
            vvsfs_free_block(&filedata);
            return -ENOMEM;
        }
        //Decompress the data.
        lz4_decompress((const unsigned char *)filedata->data,
                       &file_inode.c_size,
                       decompress_cache,
                       file_inode.size);
        //Set the read source to decompress cache.
        read_src=decompress_cache;
    } else {
        //Read from source data.
        read_src=filedata->data;
    }
    start = *buf;
    printk("rr\n");
    size = MIN(file_inode.size - *ppos,
               (*count));

    printk("readblock : %zu\n", size);
    offset = *ppos;
    *ppos += size;

    if(to_user){
        if(DEBUG) printk("r copy_to_user\n");
        if (copy_to_user(*buf, read_src + offset, size)) {
            vvsfs_free_block(&filedata);
            return -EIO;
        }
        *buf += size;
    } else {
        if(DEBUG) printk("r memcpy\n");
        memcpy(*buf, read_src + offset, size);
    }
    //Check decompress cache.
    if(decompress_cache) {
        kfree(decompress_cache);
    }

    vvsfs_free_block(&filedata);
    printk("r return\n");
    return size;
}

// vvsfs_file_read - read data from a file
static ssize_t
vvsfs_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
    struct inode *inode = filp->f_dentry->d_inode;
#else
    struct inode *inode = filp->f_path.dentry->d_inode;
#endif
    ssize_t ret;
    if (!inode) {
        printk("vvsfs - Problem with file inode\n");
        return -EINVAL;
    }
    if (!(S_ISREG(inode->i_mode))) {
        printk("vvsfs - not regular file\n");
        return -EINVAL;
    }

    //Read the content.
    ret=vvsfs_file_read_content(inode, &buf, &count, ppos, true);
    return ret;
}

static long vvsfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
    struct dentry *dentry = filp->f_dentry;
#else
    struct dentry *dentry = filp->f_path.dentry;
#endif
    struct inode *inode =dentry->d_inode;
    struct super_block *sb=inode->i_sb;
    struct vvsfs_mounted_info *mounted_info=
            (struct vvsfs_mounted_info *)sb->s_fs_info;
    int encrypt_env=mounted_info->flag & VVSFS_MOUNT_FLAG_ENCRYPT;
    char encrypt_original_key[32];
    char *file_content=NULL;
    int file_content_size=0, err=0,
            encrypt_original_key_length=mounted_info->aes_key_length;
    loff_t file_offset=0;
    struct vvsfs_ioctl *ioctl_data=NULL, *user_data=(struct vvsfs_ioctl *)arg;
    struct iattr dentry_attribute;
    
    if(DEBUG) printk("vvsfs - ioctl\n");
    //Check arg.
    if(user_data!=NULL) {
        //Set the ioctl data.
        ioctl_data=(struct vvsfs_ioctl *)kmalloc(sizeof(struct vvsfs_ioctl), 
                                                 GFP_KERNEL);
        //Check pointer.
        if(ioctl_data==NULL) {
            return -ENOMEM;
        }
        //Copy from user.
        copy_from_user(ioctl_data, user_data, sizeof(struct vvsfs_ioctl));
    }
    switch(cmd)
    {
    case VVSFS_IOCTL_DECRYPT:
        if(DEBUG) printk("ioctl - Decrypt file.\n");
        //Set encrypted flag to true.
        mounted_info->flag |= VVSFS_MOUNT_FLAG_ENCRYPT;
        //Check arg.
        if(ioctl_data!=NULL) {
            if(DEBUG) printk("Set user key, key length: %d\n", sizeof(encrypt_original_key));
            //Backup the aes key.
            memcpy(encrypt_original_key, 
                   mounted_info->aes_key, 
                   sizeof(encrypt_original_key));
            //Set the new aes key.
            memcpy(mounted_info->aes_key,
                   ioctl_data->key,
                   sizeof(encrypt_original_key));
            mounted_info->aes_key_length=32;
        }
        //Read the file content.
        if(vvsfs_file_read_content(inode, 
                                   &file_content, 
                                   &file_content_size, 
                                   &file_offset,
                                   false)<0) {
            //Check pointer.
            if(file_content) {
                //Free the dir data.
                kfree(file_content);
            }
            return -EINVAL;
        }
        //Remove the encrypted flag.
        mounted_info->flag &= ~VVSFS_MOUNT_FLAG_ENCRYPT;
        //Write the file content.
        file_offset=0;
        err=vvsfs_file_write_content(inode, 0, (char *)file_content,
                                     file_content_size, &file_offset, 
                                     false);
        printk("write result: %d\n", err);
        kfree(file_content);
        file_content=NULL;
        file_content_size=0;
        //Restore the environment.
        mounted_info->flag |= encrypt_env;
        if(ioctl_data!=NULL) {
            //Copy the original data.
            memcpy(mounted_info->aes_key,
                   encrypt_original_key,
                   sizeof(encrypt_original_key));
            mounted_info->aes_key_length=encrypt_original_key_length;
        }
        break;
    case VVSFS_IOCTL_ENCRYPT:
        if(DEBUG) printk("ioctl - Encrypt file.\n");
        //File system must have key before if the file is encrypted.
        //Read the file content.
        if(vvsfs_file_read_content(inode,
                                   &file_content,
                                   &file_content_size,
                                   &file_offset,
                                   false)<0) {
            //Check pointer.
            if(file_content) {
                //Free the dir data.
                kfree(file_content);
            }
            return -EINVAL;
        }
        //Set encrypted flag to true.
        mounted_info->flag |= VVSFS_MOUNT_FLAG_ENCRYPT;
        //Check arg.
        if(ioctl_data!=NULL) {
            if(DEBUG) printk("Set user key, key length: %d\n", sizeof(encrypt_original_key));
            //Backup the aes key.
            memcpy(encrypt_original_key,
                   mounted_info->aes_key,
                   sizeof(encrypt_original_key));
            //Set the new aes key.
            memcpy(mounted_info->aes_key,
                   ioctl_data->key,
                   sizeof(encrypt_original_key));
            mounted_info->aes_key_length=32;
        }
        //Write the file content.
        file_offset=0;
        err=vvsfs_file_write_content(inode, 0, (char *)file_content,
                                     file_content_size, &file_offset,
                                     false);
        printk("write result: %d\n", err);
        kfree(file_content);
        file_content=NULL;
        file_content_size=0;
        //Set encrypted flag to true.
        mounted_info->flag &= ~VVSFS_MOUNT_FLAG_ENCRYPT;
        //Restore the environment.
        mounted_info->flag |= encrypt_env;
        if(ioctl_data!=NULL) {
            //Copy the original data.
            memcpy(mounted_info->aes_key,
                   encrypt_original_key,
                   sizeof(encrypt_original_key));
            mounted_info->aes_key_length=encrypt_original_key_length;
        }
        break;
    case VVSFS_IOCTL_RESIZE:
        //Configure the attribute.
        dentry_attribute.ia_valid=0;
        dentry_attribute.ia_valid |= ATTR_SIZE;
        dentry_attribute.ia_size = ioctl_data->file_size;
        //Set attribute.
        vvsfs_setattr(dentry, &dentry_attribute);
        break;
    default:
        return -EINVAL;
    }
    kfree(ioctl_data);
    if(DEBUG) printk("vvsfs - ioctl done.\n");
    return 0;
}

static struct file_operations vvsfs_file_operations = {
    read:   vvsfs_file_read,        /* read  */
    write:  vvsfs_file_write,       /* write */
    open:   vvsfs_file_open,        /* open  */
    release:vvsfs_file_close,       /* close */
    mmap:   vvsfs_file_mmap,        /* mmap  */
    unlocked_ioctl:vvsfs_ioctl,     /* ioctl */
    compat_ioctl:vvsfs_ioctl,       /* ioctl */
};

static struct inode_operations vvsfs_file_inode_operations = {
    setattr:    vvsfs_setattr,          /* setattr */
};

static struct inode_operations vvsfs_special_inode_operations = {
    setattr:       vvsfs_setattr,          /*  setattr */
};

static struct inode_operations vvsfs_file_symbol_link_operations = {
    readlink:    generic_readlink,          /* read symbol */
    follow_link: vvsfs_follow_link,         /* follow link */
    setattr:     vvsfs_setattr,             /* set attr    */
};

static struct file_operations vvsfs_dir_operations = {
    #if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
    .readdir =  vvsfs_readdir,          /* readdir */
    #else
    .llseek =   generic_file_llseek,
    .read =     generic_read_dir,
    .fsync =    generic_file_fsync,
    .iterate =  vvsfs_readdir,
    .unlocked_ioctl=vvsfs_ioctl,     /* ioctl */
    .compat_ioctl=vvsfs_ioctl,       /* ioctl */
    #endif
};

static struct inode_operations vvsfs_dir_inode_operations = {
    create:     vvsfs_create,           /* create          */
    lookup:     vvsfs_lookup,           /* lookup          */
    unlink:     vvsfs_unlink,           /* unlink          */
    mkdir:      vvsfs_mkdir,            /* mkdir           */
    rmdir:      vvsfs_unlink,           /* rmdir -> unlink */
    setattr:    vvsfs_setattr,          /* setattr         */
    link:       vvsfs_link,             /* link            */
    symlink:    vvsfs_symlink,          /* symlink         */
    mknod:      vvsfs_mknod,            /* mknod           */
    rename:     vvsfs_rename,           /* rename          */
};

// vvsfs_iget - get the inode from the super block
static struct inode *vvsfs_iget(struct super_block *sb, unsigned long ino)
{
    struct inode *inode;
    struct vvsfs_inode file_inode;

    if (DEBUG) {
        printk("vvsfs - iget - ino : %d", (unsigned int) ino);
        printk(" super %p\n", sb);
    }

    inode = iget_locked(sb, ino);
    if(!inode)
        return ERR_PTR(-ENOMEM);
    if(!(inode->i_state & I_NEW))
        return inode;
    //Get the file inode.
    vvsfs_get_inode(inode, &file_inode);
    //Set inode information.
    inode->i_uid.val      = file_inode.uid;
    inode->i_gid.val      = file_inode.gid;
    inode->i_mode         = file_inode.mode;
    inode->i_ctime.tv_sec = file_inode.time_create;
    inode->i_mtime.tv_sec = file_inode.time_modify;
    inode->i_atime.tv_sec = file_inode.time_access;
    //Check inode mode.
    if(S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
        //Set the device file inode as 0.
        inode->i_size    = 0;
        //Size is not its file mode.
        init_special_inode(inode, inode->i_mode, file_inode.c_size);
    } else {
        inode->i_size    = file_inode.size;
    }
    //Print inodes.
    if(DEBUG) printk("File inode mode: %d\n", file_inode.mode);
    //Check operations.
    vvsfs_set_operations(inode);

    //Unlock the inode.
    unlock_new_inode(inode);
    return inode;
}

// vvsfs_fill_super - read the super block (this is simple as we do not
//                    have one in this file system)
static int vvsfs_fill_super(struct super_block *sb, void *data, int silent)
{
    struct inode *i;
    struct buffer_head *bh;
    struct vvsfs_mounted_info *mounted_info=NULL;
    struct vvsfs_block *journal_block=NULL;
    struct vvsfs_journal_state *journal_state=NULL;
    int hblock, device_block_size;

    if (DEBUG) printk("vvsfs - fill super\n");

    //Allocate mounted information.
    mounted_info=(struct vvsfs_mounted_info *)kmalloc(
                sizeof(struct vvsfs_mounted_info), GFP_KERNEL);
    if(mounted_info==NULL) {
        printk("no enough memory for mounted info.");
        //No enough memory.
        return -ENOMEM;
    }
    //Check the device block size first.
    hblock = bdev_logical_block_size(sb->s_bdev);
    //Read from super block.
    bh = __bread(sb->s_bdev, BLOCK_DEVICE, hblock);
    //Copy the data to the block cache.
    memcpy((void *) &mounted_info->device_info,
           (void *) bh->b_data,
           sizeof(struct vvsfs_device_block));
    //Release the buf.
    brelse(bh);
    //Get the block size.
    printk("Block size: %d\n", mounted_info->device_info.block_size);
    device_block_size=mounted_info->device_info.block_size;
    //Compare the device block and the real block.
    if (hblock > device_block_size) {
        printk("device blocks are too small!!");
        kfree(mounted_info);
        return -1;
    }
    set_blocksize(sb->s_bdev, device_block_size);
    //Add mounted info to the list.
    mounted_info->next=mount_list;
    mount_list=mounted_info;
    //Set information to mounted info.
    mounted_info->sb=sb;
    mounted_info->compress_cache=NULL;
    mounted_info->flag=0;
    mounted_info->block_content_size=device_block_size-BLOCKINFOSIZE;
    mounted_info->aes_key_length=0;
    mounted_info->ssd_start_empty_block=
            mounted_info->device_info.inode_block_count+BLOCK_INODE_LIST;

    //Construct the root directory inode.
    i = new_inode(sb);
    i->i_sb = sb;
    i->i_ino = 0;
    i->i_flags = 0;
    i->i_mode = S_IRUGO|S_IWUGO|S_IXUGO|S_IFDIR;
    vvsfs_set_operations(i);
    printk("inode %p\n", i);

    //Set device super block.
    sb->s_blocksize = device_block_size;
    sb->s_blocksize_bits = BLOCKSIZE_BITS;
    sb->s_root = d_make_root(i);
    sb->s_flags = MS_NOSUID | MS_NOEXEC;
    sb->s_op = &vvsfs_ops;
    sb->s_fs_info = mounted_info;

    //Get memory for the journal.
    journal_block=vvsfs_alloc_block(sb);
    //Check the block.
    if(journal_block) {
        //Load the data from journal block.
        vvsfs_readrawblock(sb, BLOCK_JOURNAL_STATE, journal_block);
        //Check the state.
        journal_state=(struct vvsfs_journal_state *)journal_block;
        if(journal_state->state == JOURNAL_WRITING) {
            //Revert the operation.
            //Recover the inode data.
            __vvsfs_set_inode(sb, journal_state->inode_number,
                              &(journal_state->inode));
            //Save the block number.
            hblock=journal_state->inode.block;
            //Reset the file block data.
            vvsfs_readrawblock(sb, BLOCK_JOURNAL_DATA, journal_block);
            vvsfs_writerawblock(sb, hblock, journal_block);
            //Reset the journal flag.
            vvsfs_readrawblock(sb, BLOCK_JOURNAL_STATE, journal_block);
            journal_state->state=JOURNAL_NONE;
            vvsfs_writerawblock(sb, BLOCK_JOURNAL_STATE, journal_block);
        }
        //Free the block.
        vvsfs_free_block(&journal_block);
    }
    return 0;
}

//vvsfs_kill_block_super - remove the mounted info, kill super.
static void vvsfs_kill_block_super(struct super_block *sb) {
    struct vvsfs_mounted_info *mounted_info=sb->s_fs_info;
    struct vvsfs_mounted_info *current_info=mount_list;
    //Check the head is the mounted one or not.
    if(mount_list==mounted_info) {
        //Free the compress cache.
        if(mounted_info->compress_cache!=NULL) {
            kfree(mounted_info->compress_cache);
            mounted_info->compress_cache=NULL;
        }
        //Move the head.
        mount_list=mounted_info->next;
        //Clear the mounted info.
        kfree(mounted_info);
    } else {
        //Find the mounted info in the list.
        while(current_info->next != NULL &&
              current_info->next != mounted_info) {
            //Move to next node.
            current_info=current_info->next;
        }
        //Now the next of the current info should be the target.
        if(current_info->next != NULL) {
            //Remove the node from linked list.
            current_info->next=current_info->next->next;
            //Free the compress cache.
            if(mounted_info->compress_cache!=NULL) {
                kfree(mounted_info->compress_cache);
                mounted_info->compress_cache=NULL;
            }
            //Free the mounted info.
            kfree(mounted_info);
        }
    }
    //Kill super block.
    kill_block_super(sb);
}

static struct super_operations vvsfs_ops = {
    statfs: vvsfs_statfs,
    put_super: vvsfs_put_super,
};

static struct dentry *vvsfs_mount(struct file_system_type *fs_type,
                                  int flags, const char *dev_name, void *data)
{
    return mount_bdev(fs_type, flags, dev_name, data, vvsfs_fill_super);
}

static struct file_system_type vvsfs_type = {
    .owner      = THIS_MODULE,
    .name       = "vvsfs",
    .mount      = vvsfs_mount,
    .kill_sb    = vvsfs_kill_block_super,
    .fs_flags   = FS_REQUIRES_DEV,
};

// vvsfs_get_unit - translate size from bytes to upper units.
static void vvsfs_get_unit(int bytes, int *unit_value, char **unit){
    int unit_start=0;
    while(bytes > 1023 && unit_start < 3) {
        //Reduce the bytes.
        bytes = (bytes-1024)/1024+1;
        //Increase the unit.
        ++unit_start;
    }
    //Set the unit value.
    (*unit_value)=bytes;
    (*unit)=vvsfs_storage_unit[unit_start];
}

static inline void vvsfs_proc_show_header(struct seq_file *m,
                                          char *item_name) {
    seq_printf(m, PROC_ITEM_HEADER, item_name);
}

static inline void vvsfs_proc_show_content (struct seq_file *m,
                                            char *item_name,
                                            char *content) {
    vvsfs_proc_show_header(m, item_name);
    seq_printf(m, "%s\n", content);
}

static inline void vvsfs_proc_show_value (struct seq_file *m,
                                          char *item_name, int value,
                                          char *additional_text) {
    vvsfs_proc_show_header(m, item_name);
    seq_printf(m, "%d%s\n", value, additional_text);
}

static inline void vvsfs_proc_show_size (struct seq_file *m,
                                         char *item_name, int bytes) {
    char *unit=NULL;
    int unit_value=0;

    vvsfs_proc_show_header(m, item_name);
    if(bytes<1023) {
        seq_printf(m, "%d bytes\n",
                   bytes);
    } else {
        vvsfs_get_unit(bytes,
                       &unit_value,
                       &unit);
        seq_printf(m, "%d %s, %d bytes\n",
                   unit_value, unit, bytes);
    }
}

static int vvsfs_proc_show(struct seq_file *m, void *v) {
    struct vvsfs_mounted_info *mount_node;
    struct vvsfs_block *block_data=NULL;
    int mount_counter=0, used_bytes, root_index, i, j, free_bytes,
            data_total_bytes, inode_per_block, inode_total, inode_used,
            used_block, data_blocks_total;
    struct vvsfs_inode *inode;

    //Show the title of the proc entry.
    seq_printf(m, HEADER_IMAGES, "Disk Status Viewer",
               VER_MAJOR, VER_MINOR, VER_PATCH, VER_SPECIAL);
    //Check the mounting disk status.
    if(mount_list==NULL) {
        seq_printf(m, "No mounted vvsfs drive.\n");
        return 0;
    }
    //Start to check all the items.
    mount_node=mount_list;
    while(mount_node!=NULL){
        root_index=BLOCK_INODE_LIST+mount_node->device_info.inode_block_count;
        //Allocate block data.
        block_data=vvsfs_alloc_block(mount_node->sb);
        if(block_data==NULL) {
            continue;
        }
        used_bytes=0;
        used_block=0;
        for(i=root_index;
            i<mount_node->device_info.block_count;
            ++i) {
            //Calculate the used data.
            vvsfs_readrawblock(mount_node->sb,
                               i,
                               block_data);
            if(!(block_data->flag & VVSFS_FLAG_ISEMPTY)) {
                ++used_block;
                used_bytes+=block_data->size;
            }
        }
        data_blocks_total=mount_node->device_info.block_count-root_index+1;
        data_total_bytes=(mount_node->device_info.block_count-root_index+1)*
                mount_node->block_content_size;
        free_bytes=data_total_bytes-used_bytes;
        inode_per_block=mount_node->device_info.block_size /
                        sizeof(struct vvsfs_inode);
        inode_total=inode_per_block * mount_node->device_info.inode_block_count;
        inode_used=0;
        for(i=BLOCK_INODE_LIST; i<root_index; ++i) {
            //Read the raw block.
            vvsfs_readrawblock(mount_node->sb,
                               i,
                               block_data);
            inode=(struct vvsfs_inode *)block_data;
            //Set the inode pointer.
            for(j=0; j<inode_per_block; ++j) {
                if(inode->flag & VVSFS_IFLAG_ISUSED) {
                    ++inode_used;
                }
                ++inode;
            }
        }
        //Shows the disk information.
        seq_printf(m,
                   "Disk #%d\n",
                   mount_counter);
        vvsfs_proc_show_content(m, "Encryption       ",
                                (mount_node->flag & VVSFS_MOUNT_FLAG_ENCRYPT)?
                                    "Yes" : "No");
        vvsfs_proc_show_content(m, "Compression      ",
                                (mount_node->flag & VVSFS_MOUNT_FLAG_COMPRESS)?
                                    "Yes" : "No");
        vvsfs_proc_show_content(m, "SSD Optimization ",
                                (mount_node->flag & VVSFS_MOUNT_FLAG_SSDWRITE)?
                                    "Yes" : "No");
        vvsfs_proc_show_size(m, "Device Space     ", mount_node->device_info.device_size);
        vvsfs_proc_show_size(m, "Data Space       ", data_total_bytes);
        vvsfs_proc_show_size(m, "Used             ", used_bytes);
        vvsfs_proc_show_size(m, "Free             ", free_bytes);
        vvsfs_proc_show_value(m, "Used%            ",
                              (int)((long)used_bytes*100/free_bytes), "%");
        vvsfs_proc_show_value(m, "Unit             ", mount_node->device_info.block_size, " bytes block");
        vvsfs_proc_show_value(m, "SSD Start Block  ", mount_node->ssd_start_empty_block, "");
        vvsfs_proc_show_value(m, "Inode Blocks     ",
                              mount_node->device_info.inode_block_count,
                              " blocks");
        vvsfs_proc_show_value(m, "inodes per Block ",
                              inode_per_block,
                              " inodes");
        vvsfs_proc_show_value(m, "inode Total      ", inode_total, " inodes");
        vvsfs_proc_show_value(m, "inode Used       ", inode_used, " inodes");
        vvsfs_proc_show_value(m, "inode Free       ", inode_total-inode_used,
                              " inodes");
        vvsfs_proc_show_value(m, "inode Usage      ", inode_used*100/inode_total,
                              "%");
        vvsfs_proc_show_value(m, "Data Blocks Total",
                              data_blocks_total, " blocks");
        vvsfs_proc_show_value(m, "Data Blocks Used ",
                              used_block, " blocks");
        vvsfs_proc_show_value(m, "Data Blocks Free ",
                              data_blocks_total-used_block, " blocks");
        vvsfs_proc_show_value(m, "Data Blocks Usage",
                              used_block*100/data_blocks_total, "%");
        seq_printf(m, "\n");
        //Free the block data.
        vvsfs_free_block(&block_data);
        block_data=NULL;
        //Move to next node.
        ++mount_counter;
        mount_node=mount_node->next;
    }
    return 0;
}

static int vvsfs_proc_open(struct inode *inode, struct file *file) {
    return single_open(file, vvsfs_proc_show, NULL);
}

static int vvsfs_proc_text_index_of(const char *text,
                                    int text_length,
                                    char target_text,
                                    int start_position) {
    int i;
    //Check the start position.
    if(start_position==-1) {
        start_position=0;
    } else if(start_position >= text_length) {
        //End of the text.
        return -1;
    }
    //Start looping.
    for(i=start_position; i<text_length; ++i) {
        if(text[i]==target_text) {
            return i;
        }
    }
    return -1;
}

static long vvsfs_proc_write_disk_index(char *command_cache,
                                        int start_pos,
                                        int length,
                                        char term_char) {
    long disk_index=0;
    int i;
    for(i=start_pos; i<length; ++i) {
        if(command_cache[i]==term_char) {
            break;
        } else if(command_cache[i]>='0' && command_cache[i]<='9') {
            disk_index=disk_index*10+command_cache[i]-'0';
        } else {
            printk("Invalid disk index. Check the index in /proc/vvsfs\n");
            return -1;
        }
    }
    return disk_index;
}

static void vvsfs_proc_write_set(char *command_cache,
                                 int length) {
    //Check the space position.
    int command_space_pos=-1, index_space_pos=-1, i, j;
    long disk_index=-1;
    struct vvsfs_mounted_info *mounted_info=mount_list;
    //Find the space.
    command_space_pos=vvsfs_proc_text_index_of(command_cache,
                                               length,
                                               ' ',
                                               0);
    //Check the space pos.
    if(command_space_pos==-1) {
        printk("Invalid enable parameter.\n"
               "Usage: Set <command> <disk index> <value>\n");
        return;
    }
    //Set all letter to lower letter.
    for(i=0; i<command_space_pos; ++i) {
        if(command_cache[i] > 64 && command_cache[i]<91) {
            command_cache[i]+=32;
        }
    }
    //Check the command cache.
    switch(command_space_pos) {
    case 3:
        if(strncmp(command_cache, "key", 3)==0) {
            //Find the next space.
            index_space_pos=vvsfs_proc_text_index_of(command_cache, length, ' ',
                                                     command_space_pos+1);
            //Check index space pos.
            if(index_space_pos==-1) {
                printk("Invalid disk index, the format of the command is\n"
                       "  Set Key <disk index> <key content>\n");
                return;
            }
            //Get the disk index.
            disk_index=
                    vvsfs_proc_write_disk_index(command_cache,
                                                command_space_pos+1,
                                                index_space_pos,
                                                ' ');
            //Check disk index.
            if(disk_index==-1) {
                return;
            }
            if(DEBUG) printk("vvsfs - select disk %ld\n", disk_index);
            //Loop until disk_index is 0.
            while((mounted_info!=NULL) && (disk_index>0)) {
                mounted_info=mounted_info->next;
                --disk_index;
            }
            //Check the mounted info.
            if(mounted_info==NULL) {
                printk("Cannot find disk. Check the index in /proc/vvsfs\n");
                return;
            }
            //All the other things are keys.
            if(index_space_pos==length-1) {
                mounted_info->aes_key_length=0;
            } else {
                ++index_space_pos;
                //Calculate key length.
                j=length-index_space_pos;
                //Check j's length.
                for(i=0; i<32; ++i) {
                    mounted_info->aes_key[i]=
                            command_cache[index_space_pos+(i%j)];
                }
                //Update key length.
                mounted_info->aes_key_length=32;
                printk("AES-256 raw key: %s\n", mounted_info->aes_key);
            }
            return;
        }
    default:
        printk("Invalid enable command, the command should be one of the following:\n"
               "  Key                   Set AES-256 encryption key\n");
        return;
    }
}

static void vvsfs_proc_write_switch(char *command_cache,
                                    int length,
                                    bool enabled) {
    //Check the space position.
    int command_space_pos=-1, i;
    long disk_index=-1;
    struct vvsfs_mounted_info *mounted_info=mount_list;
    int flag=0;
    //Find the space.
    command_space_pos=vvsfs_proc_text_index_of(command_cache,
                                               length,
                                               ' ',
                                               0);
    //Check the space pos.
    if(command_space_pos==-1) {
        printk("Invalid enable parameter.\n"
               "Usage: Enable/Disable <command> <disk index>\n");
        return;
    }
    //Set all letter to lower letter.
    for(i=0; i<command_space_pos; ++i) {
        if(command_cache[i] > 64 && command_cache[i]<91) {
            command_cache[i]+=32;
        }
    }
    //All the left data should be encryption disk number.
    //Check length.
    if(command_space_pos>=length) {
        printk("No disk index. The format should be:\n"
               "  Enable/Disable <command> <disk index>\n");
        return;
    }
    //Check the command cache.
    switch(command_space_pos) {
    case 10:
        //Check the command.
        if(strncmp(command_cache, "encryption", 10)==0) {
            flag=VVSFS_MOUNT_FLAG_ENCRYPT;
            break;
        }
    case 11:
        if(strncmp(command_cache, "compression", 11)==0) {
            flag=VVSFS_MOUNT_FLAG_COMPRESS;
            break;
        }
    case 16:
        if(strncmp(command_cache, "ssd_optimization", 16)==0) {
            flag=VVSFS_MOUNT_FLAG_SSDWRITE;
            break;
        }
    default:
        printk("Invalid enable command, the command should be one of the following:\n"
               "  Encryption            Disk/volume encryption\n"
               "  Compression           Disk/volume compression\n"
               "  SSD_Optimization      SSD writing optimization\n");
        return;
    }
    //Translate the number.
    disk_index=vvsfs_proc_write_disk_index(command_cache, command_space_pos+1,
                                           length, '\n');
    if(disk_index==-1) {
        return;
    }
    if(DEBUG) printk("Select disk %ld\n", disk_index);
    //Loop until disk_index is 0.
    while((mounted_info!=NULL) && (disk_index>0)) {
        mounted_info=mounted_info->next;
        --disk_index;
    }
    //Check the mounted info.
    if(mounted_info==NULL) {
        printk("Cannot find disk. Check the index in /proc/vvsfs\n");
        return;
    }
    //Get the mounted info, Set the flag.
    if(enabled) {
        //Check flag.
        if(flag==VVSFS_MOUNT_FLAG_COMPRESS) {
            mounted_info->compress_cache=
                    (char *)kmalloc(LZ4_MEM_COMPRESS, GFP_KERNEL);
            if(mounted_info->compress_cache==NULL) {
                return;
            }
        }
        mounted_info->flag |= flag;
        if(DEBUG) printk("vvsfs - Enabled disk features.\n");
    } else {
        if(flag==VVSFS_MOUNT_FLAG_COMPRESS) {
            if(mounted_info->compress_cache!=NULL) {
                kfree(mounted_info->compress_cache);
                mounted_info->compress_cache=NULL;
            }
        }
        mounted_info->flag &= ~flag;
        if(DEBUG) printk("vvsfs - Disabled disk features.\n");
    }
    return;
}

static ssize_t vvsfs_proc_write(struct file *file,
                                const char __user *user_space,
                                size_t user_space_size,
                                loff_t *offset) {
    //Allocate memory for holding user space content.
    char *command_cache=NULL;
    int command_space_pos=-1, i;

    if(DEBUG) printk("vvsfs - Disk Utility Tool\n");
    //Copy commands.
    command_cache=(char *)kmalloc(user_space_size, GFP_KERNEL);
    if(command_cache==NULL){
        return -ENOMEM;
    }
    //Copy the data from user.
    copy_from_user(command_cache, user_space, user_space_size);
    //Find the first space.
    command_space_pos=vvsfs_proc_text_index_of(command_cache,
                                               user_space_size,
                                               ' ',
                                               0);
    if(command_space_pos==-1) {
        kfree(command_cache);
        printk("Invalid parameters.\n\n"
               "Usage:\n"
               "Disable     Disable one feature for a volume.\n"
               "Enable      Enable one feature for a volume.\n"
               "Set         Set one variable for a volume.\n");
        return user_space_size;
    }
    //Set all letter to lower letter.
    for(i=0; i<command_space_pos; ++i) {
        if(command_cache[i] > 64 && command_cache[i]<91) {
            command_cache[i]+=32;
        }
    }
    //Check the command.
    switch(command_space_pos) {
    case 3:
        if(strncmp(command_cache, "set", 3)==0) {
            vvsfs_proc_write_set(command_cache+4, user_space_size-4);
        }
    case 6:
        if(strncmp(command_cache, "enable", 6)==0) {
            vvsfs_proc_write_switch(command_cache+7, user_space_size-7, true);
        }
        break;
    case 7:
        if(strncmp(command_cache, "disable", 7)==0) {
            vvsfs_proc_write_switch(command_cache+8, user_space_size-8, false);
        }
    default:
        break;
    }
    //Free the cache.
    kfree(command_cache);
    return user_space_size;
}

static const struct file_operations vvsfs_proc_fops = {
    .open       = vvsfs_proc_open,
    .read       = seq_read,
    .write      = vvsfs_proc_write,
    .llseek     = seq_lseek,
    .release    = single_release
};

static int __init vvsfs_init(void)
{
    printk("Registering vvsfs\n");
    //Initial the proc entry.
    proc_create("vvsfs", S_IRUGO | S_IWUGO, NULL, &vvsfs_proc_fops);
    return register_filesystem(&vvsfs_type);
}

static void __exit vvsfs_exit(void)
{
    printk("Unregistering the vvsfs.\n");
    //Free the proc entry.
    remove_proc_entry("vvsfs", NULL);
    unregister_filesystem(&vvsfs_type);
}

module_init(vvsfs_init);
module_exit(vvsfs_exit);
MODULE_LICENSE("GPL");
